#include "MarchingCubeRender.h"

MarchingCubeRender* MarchingCubeRender::mcr = 0;

vector3 MarchingCubeRender::pos[8] = {
		vector3(0, 0, 0),					vector3(CUBE_SIZE, 0, 0),	vector3(CUBE_SIZE, 0, -CUBE_SIZE),		vector3(0, 0, -CUBE_SIZE),
		vector3(CUBE_SIZE, CUBE_SIZE, 0),	vector3(0, CUBE_SIZE, 0),	vector3(0, CUBE_SIZE, -CUBE_SIZE),		vector3(CUBE_SIZE, CUBE_SIZE, -CUBE_SIZE)	
};

MarchingCubeRender* MarchingCubeRender::getInstance(){
	if (mcr == 0){
		mcr = new MarchingCubeRender();
	}
	return mcr;
}

bool MarchingCubeRender::isSameSign(float *density, int length){
	for (int i = 0; i < length; i++){
		if (density[i] == 0){
			return false;
		}
	}
	for (int i = 1; i < length; i++){
		if (density[i] * density[0] < 0){
			return false;
		}
	}
	return true;
}

void MarchingCubeRender::calculate(){
	PointFactory::getInstance()->reset();
	TriangleFactory::getInstance()->reset();
	for (int i = 0; i < CUBE_NUM; i++){
		for (int j = 0; j < CUBE_NUM; j++){
			for (int k = 0; k < CUBE_NUM; k++){
				cubeDensity[i][j][k] = Fluid::getInstance()->getDensity(&getPosition(i, j, k)) - DENSITY_THRESHOLD_MAX;
			}
		}
	}

	for (int i = 0; i < CUBE_NUM - 1; i++){
		for (int j = 0; j < CUBE_NUM - 1; j++){
			for (int k = 0; k < CUBE_NUM - 1; k++){
				float density[8] = {cubeDensity[i][j][k], cubeDensity[i + 1][j][k], cubeDensity[i + 1][j][k + 1], cubeDensity[i][j][k + 1],
					cubeDensity[i + 1][j + 1][k], cubeDensity[i][j + 1][k], cubeDensity[i][j + 1][k + 1], cubeDensity[i + 1][j + 1][k + 1]
				};
				if (!isSameSign(density, 8)){
					calculateCube(density, getPosition(i, j, k));
				}
			}
		}
	}
	//cout<<"calculate end"<<endl;
}

vector3 MarchingCubeRender::getPosition(int i, int j, int k){
	vector3 result = position + vector3(i * CUBE_SIZE, j * CUBE_SIZE, -k * CUBE_SIZE);
	return result;
}

Point* MarchingCubeRender::calculatePoint(std::bitset<8> id, float density1, float density2, vector3 position1, vector3 position2, vector3 normal){
	vector3 result = (density1 * position2 - density2 * position1) / (density1 - density2);
	Point *point = PointFactory::getInstance()->createPoint(id, result, normal);
	return point;
}

// Vertices of the cube are indexed as follows:
//     6-----7
//    /|    /|     y  
//   / |   / |     | 
//  5--+--4  |     |
//  |  3--+--2     *----x
//  | /   | /	  /
//  |/    |/	 /
//  0-----1		z

Point* MarchingCubeRender::calculateIntersection(float density[8], int a, int b, vector3 originPos, std::bitset<8> &cubeBit){
	std::bitset<8> id;
	if (density[a] > 0){
		cubeBit.set(7 - a);
	}
	if (density[b] > 0){
		cubeBit.set(7 - b);
	}
	if (density[a] * density[b] <= 0 && density[a] != density[b]){
		vector3 normal;
		if (density[a] > 0){
			normal = pos[a] - pos[b];
		}
		if (density[b] > 0){
			normal = pos[b] - pos[a];
		}
		id.set(7 - a);
		id.set(7 - b);
		Point *point = calculatePoint(id, density[a], density[b], originPos + pos[a], originPos + pos[b], normal);
		return point;
	}
	return 0;
}

void MarchingCubeRender::calculateCube(float density[8], vector3 originPos){
	std::bitset<8> cubeBit;
	std::vector<Point *> points;
	Point *point = calculateIntersection(density, 0, 1, originPos, cubeBit);
	if (point != 0){
		points.push_back(point);
	}

	point = calculateIntersection(density, 0, 3, originPos, cubeBit);
	if (point != 0){
		points.push_back(point);
	}

	point = calculateIntersection(density, 0, 5, originPos, cubeBit);
	if (point != 0){
		points.push_back(point);
	}

	point = calculateIntersection(density, 1, 2, originPos, cubeBit);
	if (point != 0){
		points.push_back(point);
	}

	point = calculateIntersection(density, 1, 4, originPos, cubeBit);
	if (point != 0){
		points.push_back(point);
	}

	point = calculateIntersection(density, 2, 3, originPos, cubeBit);
	if (point != 0){
		points.push_back(point);
	}
	
	point = calculateIntersection(density, 2, 7, originPos, cubeBit);
	if (point != 0){
		points.push_back(point);
	}

	point = calculateIntersection(density, 3, 6, originPos, cubeBit);
	if (point != 0){
		points.push_back(point);
	}

	point = calculateIntersection(density, 4, 5, originPos, cubeBit);
	if (point != 0){
		points.push_back(point);
	}

	point = calculateIntersection(density, 4, 7, originPos, cubeBit);
	if (point != 0){
		points.push_back(point);
	}

	point = calculateIntersection(density, 5, 6, originPos, cubeBit);
	if (point != 0){
		points.push_back(point);
	}

	point = calculateIntersection(density, 6, 7, originPos, cubeBit);
	if (point != 0){
		points.push_back(point);
	}

	//call getTriangle(cubeBit);
	//cout<<"inter end triangle start"<<endl;
	std::bitset<8> *triBit = MarchingCubeTable::getInstance()->getTriangles(cubeBit);
	//call constructTriangle(std::vector<Point *> &points, std::bitset<8> triBit[12])
	//cout<<"triangle end construction start"<<endl;
	constructTriangle(points, triBit);
	//cout<<"construction end"<<endl;
}

void MarchingCubeRender::constructTriangle(std::vector<Point *> &points, std::bitset<8> triBit[12]){
	if (!triBit[0].none()){
		for (std::vector<Point *>::iterator iter = points.begin(); iter != points.end(); iter++){
			//cout<<"Point:"<<(*iter)<<" ID:"<<(*iter)->getID()<<endl;
		}
	}
	for (int triNum = 0; triNum < 4; triNum++){
		if (!(triBit[triNum * 3]).none()){
			Point *p0, *p1, *p2;
			p0 = findPointByID(points, triBit[triNum * 3]);
			//cout<<"p0 end "<<p0<<" ID:"<<triBit[triNum * 3]<<endl;
			p1 = findPointByID(points, triBit[triNum * 3 + 1]);
			//cout<<"p1 end "<<p1<<" ID:"<<triBit[triNum * 3 + 1]<<endl;
			p2 = findPointByID(points, triBit[triNum * 3 + 2]);
			//cout<<"p2 end "<<p2<<" ID:"<<triBit[triNum * 3 + 2]<<endl;
			//cout<<"p0:"<<p0->getPosition().x<<","<<p0->getPosition().y<<","<<p0->getPosition().z<<endl;
			//cout<<"p1:"<<p1->getPosition().x<<","<<p1->getPosition().y<<","<<p1->getPosition().z<<endl;
			//cout<<"p2:"<<p2->getPosition().x<<","<<p2->getPosition().y<<","<<p2->getPosition().z<<endl;
			vector3 normal = CrossProduct(p0->getPosition() - p1->getPosition(), p1->getPosition() - p2->getPosition());
			//cout<<"normal end"<<endl;
			if (DotProduct(normal, p0->getNormal()) < 0){
				normal = -normal;
				//cout<<"DotProduct end"<<endl;
			}
			//cout<<"no if end"<<endl;
			TriangleFactory::getInstance()->createTriangle(p0, p1, p2, normal);
			//cout<<"createTriangle end"<<endl;
		}
	}
}

Point* MarchingCubeRender::findPointByID(std::vector<Point *> &points, std::bitset<8> triBit){
	//cout<<"size:"<<points.size()<<endl;
	for (std::vector<Point *>::iterator iter = points.begin(); iter != points.end(); iter++){
		if ((*iter)->getID() == triBit){
			return (*iter);
		}
	}
	return 0;
}